
#include <cegfx/cemacros.h>
#include <cegfx/ceeffect.h>
#include <cegfx/ceparticelmesh.h>

ceParticelMesh::ceParticelMesh ()
	: vertexBuffer (0)
	, numVertices(0)
	, indexBuffer (0)
	, numTriangles (0)
	, vertexDeclaration(0)
	, stride (0)
	, particelRadius (0.0f)
{
	ClearParticels();
}

ceParticelMesh::~ceParticelMesh ()
{
	if (vertexBuffer)
	{
		vertexBuffer->Release ();
	}
	if (indexBuffer)
	{
		indexBuffer->Release ();
	}
	if (vertexDeclaration)
	{
		vertexDeclaration->Release ();
	}
}

IQF_IMPLEMENTATION_BEGIN (ceParticelMesh);
IQF_IMPLEMENTATION_INTERFACE(iGeometry);
IQF_IMPLEMENTATION_END ();


bool ceParticelMesh::CreateParticel (LPDIRECT3DDEVICE9 device, float w, float h)
{
	struct Vertex
	{
		float x, y, z;
		float tu, tv;
	};

	Vertex vertices [] = {
		{ -w, -h, 0,   0, 0 },
		{ -w,  h, 0,   0, 1 },
		{  w,  h, 0,   1, 1 },
		{  w, -h, 0,   1, 0 }
	};

	unsigned short indices [] = { 0, 1, 2, 0, 2, 3 };

	D3DVERTEXELEMENT9 elements [] = {
		{ 0,   0, D3DDECLTYPE_FLOAT3,		D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION,		0 },
		{ 0,  12, D3DDECLTYPE_FLOAT2,	  D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD,   0 },
		D3DDECL_END ()
	};

	if (FAILED(device->CreateVertexDeclaration (elements, &vertexDeclaration))) return false;
	if (FAILED(device->CreateVertexBuffer (sizeof (Vertex) * 4, 0, 0, D3DPOOL_DEFAULT, &vertexBuffer, 0))) return false;
	if (FAILED(device->CreateIndexBuffer (sizeof (unsigned short) * 6, 0, D3DFMT_INDEX16, D3DPOOL_DEFAULT, &indexBuffer, 0))) return false;

	void* data;
	if (SUCCEEDED(vertexBuffer->Lock (0, sizeof (Vertex) * 4, &data, 0)))
	{
		memcpy (data, vertices, sizeof (Vertex) * 4);
		vertexBuffer->Unlock();
	}
	if (SUCCEEDED(indexBuffer->Lock (0, sizeof (unsigned short) * 6, &data, 0)))
	{
		memcpy (data, indices, sizeof (unsigned short) * 6);
		indexBuffer->Unlock ();
	}

	stride = sizeof (Vertex);
	numVertices = 4;
	numTriangles = 2;

	particelRadius = sqrt (w*w + h*h);

	return true;
}


void ceParticelMesh::SetParticelRadius (float radius)
{
	particelRadius = radius;
}

void ceParticelMesh::ClearParticels ()
{
	bbox.Clear();
	positions.clear();
}

void ceParticelMesh::AddParticel (const D3DXVECTOR3& pos, const D3DXVECTOR2& trans, float rot, float alpha)
{
	positions.push_back (D3DXVECTOR4(pos.x, pos.y, pos.z, 1.0f));
	transRotAlpha.push_back (D3DXVECTOR4(trans.x, trans.y, rot, alpha));
}

void ceParticelMesh::UpdateParticel (unsigned id, const D3DXVECTOR3& pos, const D3DXVECTOR2& trans, float rot, float alpha)
{
	positions[id] = D3DXVECTOR4(pos.x, pos.y, pos.z, 1.0f);
	transRotAlpha[id] = D3DXVECTOR4(trans.x, trans.y, rot, alpha);
}

void ceParticelMesh::RemoveParticel (unsigned id)
{
	positions.erase (positions.begin () + id);
	transRotAlpha.erase (transRotAlpha.begin() + id);
}

void ceParticelMesh::UpdateBoundingBox ()
{
	bbox.Clear();

	FOR_VECTOR(unsigned, i, j, positions)
	{
		D3DXVECTOR4& v = positions[i];
		bbox.AddVector3 (v.x, v.y, v.z);
	}

	bbox.AddBorder (particelRadius, particelRadius, particelRadius);
	bbox.Update();
}


void ceParticelMesh::Render (const ceRenderEnv& env)
{
	LPDIRECT3DDEVICE9 device = env.device;
	ceEffect* effect = ceEffect::GetCurrentEffect();
	if (!effect)
	{
		return;
	}

	if (positions.size () == 0)
	{
		return;
	}

	D3DXHANDLE handlePosition = effect->GetHandleBySemantic ("PARTICELPOSITION");
	D3DXHANDLE handleTransRotAlpha = effect->GetHandleBySemantic ("PARTICELTRANSROTALPHA");

	device->SetVertexDeclaration (vertexDeclaration);
	device->SetStreamSource (0, vertexBuffer, 0, stride);
	device->SetIndices (indexBuffer);

	const ceClipper* clipper = env.clipper;

	device->SetRenderState (D3DRS_ALPHABLENDENABLE, TRUE);
	device->SetRenderState (D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
	device->SetRenderState (D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
	device->SetRenderState (D3DRS_ZWRITEENABLE, FALSE);
	device->SetRenderState (D3DRS_ZENABLE, TRUE);
	FOR_VECTOR(unsigned, i, j, positions)
	{
		D3DXVECTOR4& v = positions[i];
		//if (!clipper || clipper->Test (v, particelRadius) == CR_In)
		{
			effect->SetVector (handleTransRotAlpha, &transRotAlpha[i]);
			effect->SetVector (handlePosition, &positions[i]);
			effect->GetEffect ()->CommitChanges ();
			device->DrawIndexedPrimitive (D3DPT_TRIANGLELIST, 0, 0, numVertices, 0, numTriangles);
		}
	}

	device->SetRenderState (D3DRS_ZWRITEENABLE, TRUE);
	device->SetRenderState (D3DRS_ALPHABLENDENABLE, FALSE);
}


void ceParticelMesh::Update (const ceUpdateEnv& env)
{
}

void ceParticelMesh::SetBoundingBox (const D3DXVECTOR3& min, const D3DXVECTOR3& max)
{
	bbox.Clear ();
	bbox.AddVector3 (min);
	bbox.AddVector3 (max);
	bbox.Update();
}

const ceBoundingBox& ceParticelMesh::GetBoundingBox () const
{
	return bbox;
}

